package personal.dao.accounting;

import java.util.List;

import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.persistence.Query;

import org.springframework.stereotype.Repository;

import personal.dao.support.TransactionSearchCriteria;
import personal.domain.accounting.ExpenseTransaction;
import personal.domain.accounting.IncomeTransaction;
import personal.domain.accounting.TransferTransaction;


@Repository
public class TransactionDAOImpl implements TransactionDAO {
	
	@PersistenceContext(unitName="accountingPU")
	private EntityManager em;

	@Override
	public ExpenseTransaction saveExpenseTransaction(ExpenseTransaction transaction) {
		return em.merge(transaction);
	}

	@Override
	public IncomeTransaction saveIncomeTransaction(IncomeTransaction transaction) {
		return em.merge(transaction);
	}

	@Override
	public TransferTransaction saveTransferTransaction(TransferTransaction transaction) {
		return em.merge(transaction);
	}

	@Override
	public void deleteExpenseTransaction(Long transactionId) {
		ExpenseTransaction txn = em.getReference(ExpenseTransaction.class, transactionId);
		em.remove(txn);
	}

	@Override
	public void deleteIncomeTransaction(Long transactionId) {
		IncomeTransaction txn = em.getReference(IncomeTransaction.class, transactionId);
		em.remove(txn);
	}

	@Override
	public void deleteTransferTransaction(Long transactionId) {
		TransferTransaction txn = em.getReference(TransferTransaction.class, transactionId);
		em.remove(txn);
	}

	@SuppressWarnings("unchecked")
	@Override
	public List<ExpenseTransaction> getExpenseTransactions(TransactionSearchCriteria criteria) {
		String queryStr = "select txn from ExpenseTransaction txn ";
		queryStr += getIncomeExpenseTransactionWhereClause(criteria);
		queryStr += " order by txn.txnYear desc, txn.txnMonth desc, txn.txnDay desc";
		Query query = em.createQuery(queryStr);
		return query.getResultList();
	}

	@SuppressWarnings("unchecked")
	@Override
	public List<IncomeTransaction> getIncomeTransactions(TransactionSearchCriteria criteria) {
			String queryStr = "select txn from IncomeTransaction txn ";
			queryStr += getIncomeExpenseTransactionWhereClause(criteria);
			queryStr += " order by txn.txnYear desc, txn.txnMonth desc, txn.txnDay desc";
			Query query = em.createQuery(queryStr);
			return query.getResultList();
	}

	@SuppressWarnings("unchecked")
	@Override
	public List<TransferTransaction> getTransferTransactions(TransactionSearchCriteria criteria) {
			String queryStr = "select txn from TransferTransaction txn ";
			queryStr += getTransferTransactionWhereClause(criteria);
			queryStr += " order by txn.txnYear desc, txn.txnMonth desc, txn.txnDay desc";
			Query query = em.createQuery(queryStr);
			return query.getResultList();
	}
	
	/**
	 * Prepares basic where clause for any type of transactions based on common transaction attributes.
	 * If txnId is populated, other criteria are ignored.
	 * @param criteria - TransactionSearchCriteria
	 * @return String - prepared where clause
	 */
	private String getBasicTransactionWhereClause(TransactionSearchCriteria criteria) {
		String where = "";
		if (criteria.getTxnId() != null) {
			where = "txn.txnId=" + criteria.getTxnId();
		} else {
			where = "txn.txnYear=" + criteria.getCalendar().getYear();
			if (criteria.getCalendar().getMonth() != null) {
				where = "txn.txnMonth=" + criteria.getCalendar().getMonth() + " and " + where;
				//day is ignored if month is not populated
				if (criteria.getDay() != null) {
					where = "txn.txnDay=" + criteria.getDay() + " and " + where;
				}
			}
		}
		return where;
	}
	
	/**
	 * Prepares where clause for searching transfer transactions
	 * @param criteria - TransactionSearchCriteria
	 * @return String - prepared where clause
	 */
	private String getTransferTransactionWhereClause(TransactionSearchCriteria criteria) {
		String where = getBasicTransactionWhereClause(criteria);
		if (criteria.getTxnId() == null) {
			if (criteria.getAccountId() != null) {
				where = "(txn.fromAccountId=" + criteria.getAccountId() + " or " + "txn.toAccountId=" + criteria.getAccountId() + ") and " + where;
			}
		}
		where = "where " + where;
		return where;
	}

	/**
	 * Prepares where clause for searching income or expense transactions
	 * @param criteria - TransactionSearchCriteria
	 * @return String - prepared where clause
	 */
	private String getIncomeExpenseTransactionWhereClause(TransactionSearchCriteria criteria) {
		String where = getBasicTransactionWhereClause(criteria);
		if (criteria.getTxnId() == null) {
			if (criteria.getAccountId() != null) {
				where = "txn.accountId=" + criteria.getAccountId() + " and " + where;
			}
			if (criteria.getEntityId() != null) {
				where = "txn.entityId=" + criteria.getEntityId() + " and " + where;
			}
		}
		where = "where " + where;
		return where;
	}
}
